package com.yesep.learn.netty.protocol.util;

import java.util.List;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yesep.learn.netty.protocol.common.CN_OPT;
import com.yesep.learn.netty.protocol.common.Constants;
import com.yesep.learn.netty.protocol.common.QN_RTN;
import com.yesep.learn.netty.protocol.common.SYS_TYPE;
import com.yesep.learn.netty.protocol.exception.CommandCnException;
import com.yesep.learn.netty.protocol.exception.CommandCrcException;
import com.yesep.learn.netty.protocol.exception.CommandDataLengthException;
import com.yesep.learn.netty.protocol.exception.CommandMnException;
import com.yesep.learn.netty.protocol.exception.CommandPwException;
import com.yesep.learn.netty.protocol.exception.CommandParseException;
import com.yesep.learn.netty.protocol.exception.CommandPnumException;
import com.yesep.learn.netty.protocol.exception.CommandStException;
import com.yesep.learn.netty.protocol.impl.hj212_2017.command.Cmd9011;
import com.yesep.learn.netty.protocol.impl.hj212_2017.command.Cmd9014;
import com.yesep.learn.netty.protocol.session.Command;
import com.yesep.learn.netty.protocol.session.DataPkg;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import io.netty.channel.ChannelHandlerContext;

/**
 * @Description: 命令报文帮助类
 */
public class CommandUtil {
	private static final Logger log = LoggerFactory.getLogger(CommandUtil.class);
	/**  报文解析正则表达式	 **/
	public final static Pattern MSG_PATTERN = Pattern.compile("^" + Constants.PKG_HEADER + "(\\d{4})(.*)(.{4})");

	/**  报文解析正则表达式	 **/
	public final static Pattern CMD_PATTERN = Pattern.compile("^QN=(\\d*);ST=(\\d{2});CN=(.{4});PW=(.+);MN=(.+);Flag=(\\d{1,3});(PNUM=(\\d*);)?(PNO=(\\d*);)?CP=&&(.*)&&");

	/**   QN时间格式，精确到毫秒的时间戳	 **/
	public final static String QN_DT_FMT = "yyyyMMddHHmmssSSS";

	public static String getQN() {
		String s = DateUtil.format(DateUtil.date(), QN_DT_FMT);
		return s;
	}

	/**
	 * 数据包解包
	 * @param msg
	 * @return
	 * @throws CommandParseException
	 * @throws CommandDataLengthException
	 * @throws CommandCrcException
	 */
	public static DataPkg parsePkg(String msg) throws CommandParseException, CommandDataLengthException, CommandCrcException {
		List<String> pks = ReUtil.getAllGroups(CommandUtil.MSG_PATTERN, msg, false);
		if (pks.size() != 3 || (!NumberUtil.isInteger(pks.get(0))) || null == pks.get(1) || null == pks.get(2)) {
			throw new CommandParseException();
		}
		/**  数据段的 ASCII 字符数，例如：长 255，则写为“0255”  **/
		Integer dataLength = Integer.valueOf(pks.get(0));
		String data = pks.get(1);
		if (dataLength != data.length()) {
			throw new CommandDataLengthException();
		}
		/**
		 * 十六进制整数 
		 * 数据段的校验结果，CRC 校验算法见附录 A。接收到一条命令，如果 CRC 错误，执行结束 
		 */
		String crc = pks.get(2);
		if (!crc.equals(CommandUtil.CRC16(data))) {
			throw new CommandCrcException();
		}
		DataPkg pkg = new DataPkg();
		pkg.setData(data);
		pkg.setDataLength(dataLength);
		pkg.setCrc(crc);
		return pkg;
	}

	/**
	 * 上传命令处理
	 * @param ctx netty通道
	 * @param msg 上传命令报文
	 */
	public static void execute(ChannelHandlerContext ctx, String msg) {
		QN_RTN errorRtn = null;
		Command cmd = null;
		try {
			DataPkg pkg = parsePkg(msg);
			cmd = new Command(pkg.getData());
			if (!CommandUtil.verifyST(cmd.getST())) {
				errorRtn = QN_RTN.ST_ERROR;
				log.error(errorRtn.getMessage(), msg);
			}
			if (!CommandUtil.verifyCN(cmd.getCN())) {
				errorRtn = QN_RTN.CN_ERROR;
				log.error(errorRtn.getMessage(), msg);
			}
			if (!CommandUtil.verifyPW(cmd.getPW())) {
				errorRtn = QN_RTN.PW_ERROR;
				log.error(errorRtn.getMessage(), msg);
			}
			if (!CommandUtil.verifyMN(cmd.getMN())) {
				errorRtn = QN_RTN.MN_ERROR;
				log.error(errorRtn.getMessage(), msg);
			}
			if (cmd.isSubPkg() && (null == cmd.getPNUM() || null == cmd.getPNO())) {
				errorRtn = QN_RTN.PNUM_ERROR;
				log.error(errorRtn.getMessage(), msg);
			}
			if (errorRtn == null) {//包解析无异常
				if (cmd.isSubPkg()) {//子包处理

				}

				if (cmd.needResp()) {//需要回应
					String res = cmd.getResponse();
					if (StrUtil.isEmpty(res)) {
						res = getResponse(cmd, QN_RTN.NORMAL);
						ctx.writeAndFlush(res);
					}
				}
			}
		} catch (CommandParseException e) {
			errorRtn = QN_RTN.MSG_ERROR;
			log.error(errorRtn.getMessage(), msg);
		} catch (CommandDataLengthException e) {
			errorRtn = QN_RTN.DATA_LENGTH_ERROR;
			log.error(errorRtn.getMessage(), msg);
		} catch (CommandCrcException e) {
			errorRtn = QN_RTN.MSG_ERROR;
			log.error(errorRtn.getMessage(), msg);
		} catch (Exception e) {
			errorRtn = QN_RTN.UNKNOW;
			log.error(errorRtn.getMessage(), msg);
		}
		if (null != errorRtn) {//数据包解析有异常，怎么处理？
			if (null == cmd) {
				ctx.writeAndFlush(errorRtn.getMessage());
			} else {
				String res = getResponse(cmd, errorRtn);
			}
		}
	}

	/**
	 * 返回应答命令 
	 * @return 默认应答命令 
	 */
	public static String getResponse(Command cmd, QN_RTN QnRtn) {
		Cmd9011 res = new Cmd9011();
		res.setST(cmd.getST());
		res.setCN(cmd.getCN());
		res.setPW(cmd.getPW());
		res.setMN(cmd.getMN());
		res.setFlag(4);
		res.setQnRtn(QnRtn.getCode());
		return res.toString();
	}

	/**
	 * 验证系统类型
	 * @param ST
	 * @return
	 */
	public static boolean verifyST(String ST) {
		SYS_TYPE e = SYS_TYPE.get(ST);
		return null != e;
	}

	/**
	 * 验证命令
	 * @param CN
	 * @return
	 */
	public static boolean verifyCN(String CN) {
		CN_OPT e = CN_OPT.get(CN);
		return null != e;
	}

	/**
	 * 验证密码
	 * @param PW
	 * @return
	 */
	public static boolean verifyPW(String PW) {
		return true;
	}

	/**
	 * 验证MN
	 * @param MN
	 * @return
	 */
	public static boolean verifyMN(String MN) {
		return true;
	}

	/**
	 * @param data
	 * @return 返回CRC16大写校验码
	 * @Description: HJ212污染监测 CRC16校验算法
	 */
	public static String CRC16(String data) {
		Integer[] regs = new Integer[data.length()];
		for (int i = 0; i < data.length(); i++) {
			regs[i] = (int) data.charAt(i);
		}
		int por = 0xFFFF;
		for (int j = 0; j < regs.length; j++) {
			por = por >> 8;
			por ^= regs[j];
			for (int i = 0; i < 8; i++) {
				if ((por & 0x01) == 1) {
					por = por >> 1;
					por = por ^ 0xa001;
				} else {
					por = por >> 1;
				}
			}
		}
		String crc = Integer.toHexString(por).toUpperCase();
		return crc;
	}
}
